package yaml

import (
	"errors"
	"fmt"
	"gitee.com/dennis-kk/service-box-go/util/config/reader"
	"gitee.com/dennis-kk/service-box-go/util/config/source"
	"gopkg.in/yaml.v3"
	"reflect"
	"strconv"
	"time"
)

type (
	yamlValues struct {
		ch   *source.ChangeSet
		root *yamlValue
	}

	yamlValue struct {
		data interface{}
	}
)

func newValues(ch *source.ChangeSet) (reader.Values, error) {
	values := &yamlValues{
		ch: ch,
	}
	var data map[string]interface{}
	err := yaml.Unmarshal(values.ch.Data, &data)
	if err != nil {
		return nil, err
	}

	values.root = &yamlValue{data: data}

	return values, nil
}

func (m *yamlValue) IsNil() bool {
	return m.data == nil
}

func (m *yamlValue) Get(path string) reader.Value {
	if dm, ok := (m.data).(map[string]interface{}); ok {
		if v, ok := dm[path]; ok {
			return &yamlValue{data: v}
		}
	}
	return &yamlValue{nil}
}

func (m *yamlValue) Map() (map[string]interface{}, error) {
	if mm, ok := m.data.(map[string]interface{}); ok {
		return mm, nil
	}

	return nil, errors.New("not map type")
}

func (m *yamlValue) Bool(def bool) bool {
	if b, ok := (m.data).(bool); ok {
		return b
	}
	return def
}

func (m *yamlValue) Int(def int) int {
	if i, ok := m.data.(int); ok {
		return i
	}
	return def
}

func (m *yamlValue) String(def string) string {
	if s, ok := m.data.(string); ok {
		return s
	}
	return def
}

func (m *yamlValue) Float64(def float64) float64 {
	switch m.data.(type) {
	case float32, float64:
		return reflect.ValueOf(m.data).Float()
	case int, int8, int16, int32, int64:
		return float64(reflect.ValueOf(m.data).Int())
	case uint, uint8, uint16, uint32, uint64:
		return float64(reflect.ValueOf(m.data).Uint())
	case string:
		// 配置成string类型了尝试转一下
		if f, err := strconv.ParseFloat(m.data.(string), 64); err == nil {
			return f
		}
	}
	return def
}

func (m *yamlValue) Duration(def time.Duration) time.Duration {
	s, ok := m.data.(string)
	if !ok {
		return def
	}
	value, err := time.ParseDuration(s)
	if err != nil {
		return def
	}
	return value
}

func (m *yamlValue) StringSlice(def []string) []string {
	sa, ok := m.data.([]interface{})
	if !ok {
		return def
	}
	res := make([]string, len(sa))
	for idx, v := range sa {
		if v == nil {
			res[idx] = ""
		}
		s, ok := v.(string)
		if !ok {
			//TODO add log
			return def
		}
		res[idx] = s
	}
	return res
}

func (m *yamlValue) StringMap(def map[string]string) map[string]string {
	mm, ok := m.data.(map[string]interface{})
	if !ok {
		//TODO add error log
		return def
	}
	res := make(map[string]string, len(mm))
	for k, v := range mm {
		res[k] = fmt.Sprintf("%v", v)
	}
	return res
}

// Scan Unmarshal value to custom data
func (m *yamlValue) Scan(val interface{}) error {
	bin, err := yaml.Marshal(m.data)
	if err != nil {
		return err
	}
	return yaml.Unmarshal(bin, val)
}

func (m *yamlValue) Bytes() []byte {
	bin, err := yaml.Marshal(m.data)
	if err != nil {
		return nil
	}
	return bin
}

func (m *yamlValues) Bytes() []byte {
	return m.root.Bytes()
}

//Get will return value recursive, equal a.Get(a).Get(b).Get(c)
func (m *yamlValues) Get(path ...string) reader.Value {
	var mv reader.Value
	mv = m.root
	for _, p := range path {
		mv = mv.Get(p)
	}
	return mv
}

func (m *yamlValues) Map() map[string]interface{} {
	rm, err := m.root.Map()
	if err != nil {
		return nil
	}
	return rm
}

func (m *yamlValues) Scan(v interface{}) error {
	bin := m.root.Bytes()
	if len(bin) == 0 {
		return errors.New("config source ")
	}
	err := yaml.Unmarshal(bin, v)
	return err
}
